<script lang="ts">
  import {} from './size';
  import TileMap from '../TileMap.svelte';
  import Log from '../Log.svelte';
  import { createWorld, defineDeserializer, defineSerializer, deleteWorld, hasComponent } from 'bitecs';
  import { onMount } from 'svelte';
  import { renderTiles } from '$lib/render3';
  import { log, recentLog } from '$lib/log';
  import { setUpdate, update } from '$lib/ui';
  import { getPlayer, getTime } from '$lib';
  import { simulateWorld } from '$lib/simulate';
  import { getPos3, landscapeAt, onTile } from '$lib/pos3';
  import { WaitAction } from '$lib/wait';
  import { Collision, Creature, Glyph, Impassable, Interesting } from '$lib/components';
  import { DigAndMoveAction } from './dig';
  import CharStatus from './CharStatus.svelte';
  import TimeAndPlace from './TimeAndPlace.svelte';
  import { MoleMove, moleCanPass } from './mole';
  import { renderStack } from './render';
  import { prepareMap } from '.';
  import { Diggable, Passage, Water } from './ground';
  import Environment from '../Environment.svelte';
  import SelectedInfo from '../SelectedInfo.svelte';
  import SelectedTile from '../SelectedTile.svelte';
  import OnTile from '../OnTile.svelte';
  import { MoleAttackAndConsume, isAttackable, isTiny } from './attack';
  import { movementKeys } from '$lib/keyboard';
  import Scrollable from '../Scrollable.svelte';
  import { entityToObject, worldFromObject, worldToObject } from '$lib/serialize';
  import { saveToDb, loadFromDb } from '$lib/browserdb';
  import { addComponent, addEntity, getComponents } from '$lib/ecs';
  import { Inventory } from '$lib/inventory';
  import { decode } from '$lib/util';

  let { world, loadGame } = $props();

  let logWidget;
  let leftSideInfo;
  let tileMapWidget;

  let tileMap = $state([[{glyph:'3'}]]);
  let interesting = $state([]);
  let innerHeight = $state(0);
  let innerWidth = $state(0);
  // -0.2 is needed for some reason to avoid scrolling =\-_-/=
  let zoomlevel = $derived(Math.floor(Math.min(innerWidth, innerHeight) / 15 - 0.2));
  let theLog = $state([]);
  // force update things that are not auto-updated due to reactivity limitations
  let tick = $state(false);

  let selected = $state(undefined);

  // TODO: move to lib
  const select = (entity) => {
    if (selected === entity) {
      selected = undefined;
    } else {
      selected = entity;
    }
  };

  let inspectTile = $state(undefined);
  const selectInspectTile = (tile) => {
    inspectTile = tile;
  };

  setUpdate(() => {
    [tileMap, interesting] = renderTiles(renderStack, world, 7);
    theLog = recentLog();
    tick = !tick;
  });

  onMount(() => {
    update();
    tileMapWidget.focus();
  });

  const onKeyPress = (ev) => {
    if (ev.key === ']') {
      const focused = logWidget.focus();
      if (!focused)
        tileMapWidget.focus();
    } else if (ev.key === '[') {
      console.log('[');
      const focused = leftSideInfo.focus();
      if (!focused)
        tileMapWidget.focus();
    }
  };

  const onKeyDown = (ev) => {
    const movement = movementKeys[ev.key];
    const pc = getPlayer(world);
    let cmd;
    if (movement !== undefined && movement.some((d) => d != 0)) {
      const [dx, dy, dz] = movement;
      const {x, y, z} = getPos3(pc);
      const x1 = x+dx;
      const y1 = y+dy;
      const z1 = z+dz;
      const landscape = landscapeAt(world, x1, y1, z1);
      let diggable = false;
      const things = onTile(world, x1, y1, z1);
      const passable = moleCanPass(world, things);
      const creature = things.find((e) => hasComponent(world, Creature, e));
      if (creature) {
        if (isAttackable(world, creature, pc) && isTiny(world, creature, pc)) {
          cmd = MoleAttackAndConsume(creature);
        }
      } else if (passable) {
        cmd = MoleMove(dx, dy, dz);
      } else if (diggable) {
        cmd = DigAndMoveAction(dx, dy, dz, 20);
      } else {
        let reason = "...";
        if (hasComponent(world, Water, landscape)) {
          reason = "you can't swim in water!";
        } else if (landscape === undefined) {
          reason = "There is nothing there!";
          console.warn("Trying to go nowhere, shouldn't be possible");
        }
        log(`Cannot go there: ${reason}`);
      }
    } else if (movement !== undefined && movement.every((d) => d === 0)) {
      cmd = WaitAction(10);
    } else if (ev.key === 'S') {
      saveToDb('test', JSON.stringify(worldToObject(world))).then((res) => {
        console.log(res);
      });
    } else if (ev.key === 'Q') {
      loadFromDb('test').then((res) => {
        const obj = JSON.parse(res.content);
        console.log(obj[0]);
        deleteWorld(world);
        world = worldFromObject(obj);
        saveToDb('test2', JSON.stringify(worldToObject(world))).then((res) => {
          console.log(res);
        });
      });
    } else {
      log(`Unbound key ${ev.key}`);
    }
    if (cmd) {
      simulateWorld(world, cmd);
      selectInspectTile(undefined);
      select(undefined);
    }
    update();
  };
</script>

<svelte:window bind:innerWidth bind:innerHeight on:keypress={onKeyPress} />

<div class="main-grid">
  <div class="leftside">
    <CharStatus {tick} bind:world {zoomlevel} />
    <Scrollable bind:this={leftSideInfo} scrollToEnd={false}>
      <OnTile bind:world {select} {selected} bind:stack={interesting.currentTile} {zoomlevel} />
      <Environment bind:world {select} {selected} environment={interesting} {zoomlevel} />
      {#if inspectTile !== undefined}
        <SelectedTile bind:world {select} {selected} bind:stack={inspectTile.stack} {zoomlevel} />
      {/if}
      <SelectedInfo bind:world {selected} {zoomlevel} />
    </Scrollable>
  </div>
  <TileMap bind:this={tileMapWidget} bind:tiles={tileMap} bind:inspectTile {selectInspectTile} {select} {zoomlevel} fontFactor={0.9} {onKeyDown} />
  <div class="rightside">
    <TimeAndPlace {tick} bind:world {zoomlevel} />
    <Scrollable bind:this={logWidget} height="90vh" scrollToEnd={true}>
      <Log bind:log={theLog} {zoomlevel} />
    </Scrollable>
  </div>
</div>

<style>
  .main-grid {
    display: grid;
  }

  @media screen and (orientation: portrait) {
    .main-grid {
      grid-template-rows: 1fr 100vw 1fr;
      grid-template-areas:
        "leftside"
        "main"
        "rightside";
    }
  }

  @media screen and (orientation: landscape) {
    .main-grid {
      grid-template-columns: 1fr 100vh 1fr;
      grid-template-areas:
        "leftside main rightside";
    }
  }

  .leftside {
    grid-area: leftside;
    height: 99vh; /* for some reason 100vh still causes overflow *sometimes* */
    display: flex;
    flex-direction: column;
  }

  .righside {
    grid-area: rightside;
  }
</style>
